#include "ccom.h"
#include "circularbuffer.h"
#include <Process.h>

ccom::ccom(char * portName){

	MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, portName, (int)strlen(portName)+1, m_portName, (int)strlen(portName)+1);
	circBuffer = new ccirc(512);
	
	
}

ccom::~ccom(void){
    
}

/*
parity:
NOPARITY
EVENPARITY
ODDPARITY

stopbits:
ONESTOPBIT
ONE5STOPBITS
TWOSTOPBITS
*/
ccom::ccomError ccom::Configure(DWORD baudRate, BYTE parity, BYTE stopbits){
	DCB dcb;
	ccom::ccomError e = ccom::Ok;

	if(this->m_hSerialComm != INVALID_HANDLE_VALUE){
		GetCommState(this->m_hSerialComm, &dcb);
		dcb.Parity = parity;
		dcb.StopBits = stopbits;
		dcb.BaudRate = baudRate;
		SetCommState(this->m_hSerialComm, &dcb);
	}else{
		e = ccom::Error;
	}
	return e;
}
ccirc* ccom::getBuffer(void)
{
    return circBuffer;
}


ccom::ccomError ccom::Open(void){
	COMMTIMEOUTS timeouts;
	
	ccom::ccomError e = ccom::Ok;
	this->m_hSerialComm = CreateFile(	m_portName,
		GENERIC_READ|GENERIC_WRITE,
		0,
		NULL,
		OPEN_EXISTING,
		FILE_FLAG_OVERLAPPED,
		NULL);

	if(this->m_hSerialComm == INVALID_HANDLE_VALUE){
		e = ccom::Error;
	}

	_beginthreadex(NULL, 0, ccom::RxSerial, this, 0, NULL);
	/*The configuration hereby presented allows us to read
	char by char otherwise the algorithm in ccom::Read gets
	stuck in the last ReadFile expecting another character.
	*/

	timeouts.ReadIntervalTimeout = MAXDWORD; 
	timeouts.ReadTotalTimeoutMultiplier = 0;
	timeouts.ReadTotalTimeoutConstant = 0;
	timeouts.WriteTotalTimeoutMultiplier = 0;
	timeouts.WriteTotalTimeoutConstant = 10;

	if (!SetCommTimeouts(this->m_hSerialComm, &timeouts)) {
		e = ccom::Error;
	}

	return e;
}

ccom::ccomError ccom::Close(void){
	ccom::ccomError e = ccom::Ok;

	if(this->m_hSerialComm != INVALID_HANDLE_VALUE){
		CloseHandle(this->m_hSerialComm);
	} else {
		e = ccom::Error;
	}

	this->m_hSerialComm = INVALID_HANDLE_VALUE;

	return e;
}

unsigned int _stdcall ccom::RxSerial(void* arg)
{
	DWORD dEvMask;
	DWORD dRead=1;
	DWORD dwait=0;
	unsigned char c;
	ccom *com = (ccom*)arg;
	bool started = false;
    OVERLAPPED ovRead;
	SetCommMask(com->m_hSerialComm, EV_RXCHAR);
    //FlushFileBuffers(com->m_hSerialComm);
    com->ov.hEvent = CreateEvent( 0,true,0,0);
	for(;;) {
        

		WaitCommEvent(com->m_hSerialComm, &dEvMask, &com->ov);
		dwait = WaitForSingleObject(com->ov.hEvent, 1000);
		
        if(dwait == WAIT_OBJECT_0)
        {
		    
		    ovRead.hEvent = CreateEvent(0, true, 0,0);
		    memset(&ovRead,0,sizeof(ovRead));
		    ResetEvent(ovRead.hEvent);
    		
            while(dRead > 0){
		        if(ReadFile(com->m_hSerialComm, &c, 1, &dRead, &ovRead) > 0 && dRead > 0)
		        {
			        com->getBuffer()->addToBuffer(c);  
		        }
            }
            dRead = 1;
		    ResetEvent(com->ov.hEvent);
            
            CloseHandle(ovRead.hEvent);	
        } 

		Sleep(1);
		

	}

	

	return 0;
}

ccom::ccomError ccom::Write(const char * buffer, unsigned int size){
	DWORD bWritten;
    DWORD ret;
	ccom::ccomError e = ccom::Ok;
    OVERLAPPED writeEvent;
    writeEvent.hEvent = CreateEvent(0, true, 0 ,0);
    memset(&writeEvent,0,sizeof(writeEvent));
    if(this->m_hSerialComm !=INVALID_HANDLE_VALUE){
		ret = WriteFile(this->m_hSerialComm, (void*) buffer, size, &bWritten,&writeEvent) ;
        if(ret == 0)
        {
            WaitForSingleObject(writeEvent.hEvent, 100);
        }
        CloseHandle(writeEvent.hEvent);
		e = ccom::Ok;
	} else {
		e = ccom::Error;
	}
	return e;
}

ccom::ccomError ccom::getNextChar(unsigned char *c){
	ccom::ccomError e = ccom::Ok;

	if(circBuffer->readNext(c) != 0){
		e = ccom::Error;
	}

	return e;
}

